-
Notifications
You must be signed in to change notification settings - Fork 30k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
stream: fix readable behavior for highWaterMark === 0 #21690
Conversation
/cc @nodejs/streams |
ab7ddcd
to
d14752c
Compare
ping @mcollina. |
d14752c
to
4df321c
Compare
Well, CI has failed due to my test... I'm not sure how to handle tty stdin on CI, as it works fine locally. It seems to close the stdin even before the setTimeout fires, can someone help? Should I |
r.push(null); | ||
|
||
r.once('readable', common.mustCall()); | ||
r.once('end', common.mustCall()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When reading the description of the test it seems like these should be .on
instead of once?
const assert = require('assert'); | ||
|
||
// This test ensures that Node.js will not ignore tty 'readable' subscribers | ||
// when it's the only tty subscriber and te only thing keeping event loop alive |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typo in the
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you want to actually test a tty you have to move the test in the corresponding tty test folder. Otherwise it's not detected as try.
@BridgeAR thanks a lot, I've missed that. Should be fixed now. |
|
||
r.push(null); | ||
|
||
r.once('readable', common.mustCall()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this not also just be on
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, idk why did I think it was okay to put once on both of them. 🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
code LGTM. Can land if citgm does not report additional breakage.
Seems good, but CITGM is currently not very healthy: nodejs/build#1429. |
I'm not I understand why this is needed for TTYs to work? Can someone clarify this for me? The unneeded readables was breaking of code for me, and as I understand this, it restores that behaivor? |
@mafintosh After your comment, I went to double check and found a better way of tackling this problem. But I also discovered the other thing. Your PR is indeed fixing the issue of empty-readable but the thing is, it suppresses the issue by not emitting the event but there are still multiple calls to The thing is that with the write pattern as in
there are 2 more 'emit readable rStream' that were suppressed and I think at least the second one shouldn't have happened at all (the first one is from the latest data push). |
ce3551e
to
e874f79
Compare
@mcollina @mafintosh @BridgeAR I'd like another review as I changed this PR's idea. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add some tests that shows the changed behavior in streams? I'd like to see a a test that failed in current master but passes here.
I think we would need to add some docs for highWaterMark: 0
. There aren't any atm.
(Using the big red cross because I approved it in the past)
lib/_stream_readable.js
Outdated
@@ -274,7 +274,9 @@ function readableAddChunk(stream, chunk, encoding, addToFront, skipChunkCheck) { | |||
// Also, if we have no data yet, we can stand some more bytes. | |||
// This is to work around cases where hwm=0, such as the repl. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We'll need to adjust this comment.
@@ -383,7 +385,8 @@ Readable.prototype.read = function(n) { | |||
// the 'readable' event and move on. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this comment will need an update.
It's not really clear what it is changing here. Can you describe it the change to streams a bit more? |
Avoid trying to emit 'readable' due to the fact that state.length is always >= state.highWaterMark if highWaterMark is 0. Therefore upon .read(0) call (through .on('readable')) stream assumed that it has enough data to emit 'readable' even though state.length === 0 instead of issuing _read(). Which led to the TTY not recognizing that someone is waiting for the input. Fixes: nodejs#20503 Refs: nodejs#18372
e874f79
to
e5a6527
Compare
@mcollina cleaned up the PR (and removed redundant checks) and added relevant test without the TTY. Yeah, this shouldn't change anything, it's a bug fix, bad wording, sorry. I've updated the description. P.s. Streams sure are tough, thanks for bearing with me. |
Thanks for taking these issue, they are indeed! |
Very nice fix in the end |
Landed in fe47b8b |
Thanks @lundibundi for tackling this one! |
Avoid trying to emit 'readable' due to the fact that state.length is always >= state.highWaterMark if highWaterMark is 0. Therefore upon .read(0) call (through .on('readable')) stream assumed that it has enough data to emit 'readable' even though state.length === 0 instead of issuing _read(). Which led to the TTY not recognizing that someone is waiting for the input. Fixes: #20503 Refs: #18372 PR-URL: #21690 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de>
Avoid trying to emit 'readable' due to the fact that state.length is always >= state.highWaterMark if highWaterMark is 0. Therefore upon .read(0) call (through .on('readable')) stream assumed that it has enough data to emit 'readable' even though state.length === 0 instead of issuing _read(). Which led to the TTY not recognizing that someone is waiting for the input. Fixes: #20503 Refs: #18372 PR-URL: #21690 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de>
Avoid trying to emit 'readable' due to the fact that
state.length is always >= state.highWaterMark if highWaterMark is 0.
Therefore upon .read(0) call (through .on('readable')) stream assumed
that it has enough data to emit 'readable' even though
state.length === 0 instead of issuing _read(). Which led to the TTY
not recognizing that someone is waiting for the input.
Fixes: #20503
Refs: #18372
Checklist
make -j4 test
(UNIX) passesI put one test under sequential as it uses process.stdin to emulate the use case and I'm not sure if that is correct.
Edit: I've changed the PR title and description according to changes.